package com.jml.FenBuShiLock;


import java.util.concurrent.CountDownLatch;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
import org.apache.curator.framework.recipes.cache.PathChildrenCache.StartMode;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent.Type;
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs.Ids;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/*
 *  分布式锁实现工具类
 *
 * */
@Component
public class DistributedLock {

	private CuratorFramework client=null;//zk客户端
	static final Logger log = LoggerFactory.getLogger(DistributedLock.class);


	private static CountDownLatch zkLockLatch = new CountDownLatch(1);

	//分布式锁总结点名称
	private static final String ZK_LOCK_PROJECT="imooc-locks";
	//分布式锁节点
	private static final String DISTRIBUTED_LOCK ="distributed-lock";

	public DistributedLock(CuratorFramework client){
		this.client=client;
	}

	/*
	 * 初始化锁
	 * */
	public void init(){
		//使用命名空间
		client = client.usingNamespace("ZKLocks-Namespace");
		/*
		 * 创建zk锁的总节点
		 * ZKLocks-Namespace
		 *           |
		 *           ————imooc-locks
		 *                       |
		 *                       ————distributed-lock
		 * */
		try {
			if(client.checkExists().forPath("/"+ZK_LOCK_PROJECT)==null){
				client.create()
						.creatingParentsIfNeeded()
						.withMode(CreateMode.PERSISTENT)
						.withACL(Ids.OPEN_ACL_UNSAFE)
						.forPath("/"+ZK_LOCK_PROJECT);
			}
			//针对zk的分布式锁节点，创建相应的watcher监听事件
			addWatcherToLock("/"+ZK_LOCK_PROJECT);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}



	public void getLock(){
		while (true){
			try {
				client.create()
						.creatingParentsIfNeeded()
						.withMode(CreateMode.EPHEMERAL)//临时节点
						.withACL(Ids.OPEN_ACL_UNSAFE)
						.forPath("/"+ZK_LOCK_PROJECT+"/"+DISTRIBUTED_LOCK);//创建当前业务锁
				log.info("获得分布式锁成功....");
				return;
			} catch (Exception e) {
				//当节点存在的时候再创建就会抛出异常，表示这个节点被其他请求创建了，锁已经被其他节点获得了
				log.info("获得分布式锁失败....");
				try {
					if(zkLockLatch.getCount()<=0){
						zkLockLatch=new CountDownLatch(1);
					}
					//阻塞线程   等待获取锁
					zkLockLatch.await();
				} catch (Exception e1) {
					e1.printStackTrace();
				}
			}
		}

	}

	//释放分布式锁
	public boolean releaseLock(){
		try {
			if(client.checkExists().forPath("/"+ZK_LOCK_PROJECT+"/"+DISTRIBUTED_LOCK)!=null){//锁存在
				client.delete().forPath("/"+ZK_LOCK_PROJECT+"/"+DISTRIBUTED_LOCK);
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		//
		log.info("分布式锁释放完毕....");
		return true;
	}


	private void addWatcherToLock(String path) throws Exception{
		final  PathChildrenCache cache = new PathChildrenCache(client,path,true);
		cache.start(StartMode.POST_INITIALIZED_EVENT);
		cache.getListenable().addListener(new PathChildrenCacheListener() {
			@Override
			public void childEvent(CuratorFramework client, PathChildrenCacheEvent event)
					throws Exception {
				//监听节点的锁被删除，但是不一定是目标的锁被删除
				if(event.getType().equals(Type.CHILD_REMOVED)){
					String path=event.getData().getPath();
					log.info("上一个会话已经释放锁或该会话已经断开，节点路径为:"+path);
					//当删除的锁是订单锁的时候
					if(path.contains(DISTRIBUTED_LOCK)){
						log.info("释放计数器，让当前请求获取分布式锁....");
						zkLockLatch.countDown();
					}
				}
			}
		});



	}
}
